package com.linkoog.devtools.http.everywhere;

import com.intellij.ide.actions.SearchEverywherePsiRenderer;
import com.intellij.ide.actions.searcheverywhere.AbstractGotoSEContributor;
import com.intellij.ide.util.gotoByName.FilteringGotoByModel;
import com.intellij.navigation.ChooseByNameContributor;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.MinusculeMatcher;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.speedSearch.SpeedSearchUtil;
import com.intellij.util.ArrayUtilRt;
import com.intellij.util.text.Matcher;
import com.intellij.util.text.MatcherHolder;
import com.intellij.util.ui.UIUtil;
import com.linkoog.devtools.common.Pair;
import com.linkoog.devtools.http.everywhere.matcher.PinyinMatcher;
import com.linkoog.devtools.http.raw_index.HttpServiceClass;
import com.linkoog.devtools.http.raw_index.HttpServiceClassRawFileIndex;
import com.linkoog.devtools.http.raw_index.HttpServiceMethod;
import com.linkoog.devtools.http.raw_index.HttpServiceMethodRawFileIndex;
import com.linkoog.devtools.metadata.DocumentMetadata;
import com.linkoog.devtools.tinypinyin.pinyinhelper.Pinyin;
import com.linkoog.devtools.type.javadoc.JavaDoc;
import com.linkoog.devtools.utils.StringUtils;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class DocSearchContributor extends AbstractGotoSEContributor {

    public static final String SEARCH_PROVIDER_ID = "Http";

    private final ChooseByNameContributor contributor;


    protected DocSearchContributor(@NotNull AnActionEvent event) {
        super(event);

        this.contributor = new ChooseByNameContributor() {

            private final MultiMap<String, Pair<String,DocumentMetadata>> results = new MultiMap();

            @NotNull
            public String[] getNames(Project project, boolean includeNonProjectItems) {
                this.results.clear();

                HttpServiceClassRawFileIndex httpServiceClassRawFileIndex = HttpServiceClassRawFileIndex.getInstance(project);
                for (HttpServiceClass httpServiceClass : httpServiceClassRawFileIndex.getCaches()) {
                    String classSwaggerComment = httpServiceClass.getSwaggerComment();
                    if (StringUtils.isNotBlank(classSwaggerComment)){
                        results.put(classSwaggerComment, new Pair<>("swagger", httpServiceClass));
                    }
                }

                // 另一种方式：将文档注入到name中
                HttpServiceMethodRawFileIndex httpServiceMethodRawFileIndex = HttpServiceMethodRawFileIndex.getInstance(project);
                for (HttpServiceMethod httpServiceMethod : httpServiceMethodRawFileIndex.getCaches()) {
                    List<String> mappingUrls = httpServiceMethod.getMappingUrls();
                    if (! mappingUrls.isEmpty()) {
                        for (String mappingUrl : mappingUrls) {
                            results.put(mappingUrl, new Pair<>("url", httpServiceMethod));
                        }
                    }

                    String methodSwaggerComment = httpServiceMethod.getSwaggerComment();
                    if (StringUtils.isNotBlank(methodSwaggerComment)){
                        results.put(methodSwaggerComment,new Pair<>("swagger", httpServiceMethod));
                    }

                    JavaDoc javaDoc = httpServiceMethod.getJavaDoc();
                    if (javaDoc != null){
                        String description = javaDoc.getDescription();
                        if (StringUtils.isNotBlank(description)){
                            results.put(description, new Pair<>("javaDoc", httpServiceMethod));
                        }
                    }
                }

                return this.results.keySet().toArray(ArrayUtilRt.EMPTY_STRING_ARRAY);
            }

            @NotNull
            public NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) {
                final List<Pair<String,DocumentMetadata>> metaDatas = results.getAll(name);
                if (metaDatas.isEmpty()){
                    return new NavigationItem[0];
                }

                NavigationItem[] items = new NavigationItem[metaDatas.size()];
                for (int i = 0; i < metaDatas.size(); i++) {
                    Pair<String, DocumentMetadata> data = metaDatas.get(i);
                    items[i] = new DocSearchItem(name, data.getKey(), data.getValue());
                }

                return items;
            }

            private String toPinyin(String input){
                return Pinyin.toPinyin(input," ").toLowerCase();
            }

            /**
             *  获取所有中文的首字母拼音
             */
            private String toFristPinyin(String input){
                if (StringUtils.isBlank(input)){
                    return input;
                }

                StringBuilder builder = new StringBuilder(input.length());
                for (char item : input.toCharArray()) {
                    String pinyin = Pinyin.toPinyin(item);
                    if (! pinyin.isEmpty()){
                        builder.append(pinyin.substring(0, 1).toLowerCase());
                    }
                }
                return builder.toString();
            }
        };
    }

    private class MultiMap<K,V>{
        private Map<K,List<V>> data = new ConcurrentHashMap<K,List<V>>();

        List<V> getAll(K key){
            List<V> curVas =  data.get(key);
            return curVas!=null ? curVas: Collections.<V>emptyList();
        }

        void put(K key,V val){
            List<V> curVals = data.getOrDefault(key,new ArrayList<>(3));
            data.put(key, curVals);
            curVals.add(val);
        }

        void clear(){
            data.clear();
        }

        Set<K> keySet(){
            return data.keySet();
        }
    }

    @NotNull
    protected FilteringGotoByModel<?> createModel(@NotNull Project project) {
        return new DocFilteringGotoByModel(project, new ChooseByNameContributor[]{this.contributor});
    }

    /**
     * 搜索窗体的自定义action
     */
    /*@Override
    public List<AnAction> getActions(Runnable firstOnChanged){
        // todo 原本区分大小写、正则、单词匹配应该在编辑框中，但是为了兼容低版本
        AtomicBooleanProperty caseSensitive = new AtomicBooleanProperty(myFindModel.isCaseSensitive);
        caseSensitive.afterChange(val->{
            //myFindModel.isCaseSensitive = val;
            return Unit.INSTANCE;
        });

        AtomicBooleanProperty regexp = new AtomicBooleanProperty();
        lateinit var regexp: AtomicBooleanProperty
        val word = AtomicBooleanProperty(myFindModel.isWholeWordsOnly).apply {
            afterChange { myFindModel.isWholeWordsOnly = it; if (it) regexp.set(false) }
        }

        regexp = AtomicBooleanProperty(myFindModel.isRegularExpressions).apply {
            afterChange { myFindModel.isRegularExpressions = it; if (it) word.set(false) }
        }

        val onChanged = Runnable { searchCache.clear(); firstOnChanged.run() }
        return arrayListOf(
                CaseSensitiveAction(case, onChanged),
            WordAction(word, onChanged),
                    RegexpAction(regexp, onChanged),
                    SearchEverywhereFiltersAction(myFilter, onChanged)
        )
    }*/


    @NotNull
    @Nls
    public String getGroupName() {
        return "Http";
    }

    @Override
    public @NotNull String getSearchProviderId() {
        return SEARCH_PROVIDER_ID;
    }

    public int getSortWeight() {
        return 5;
    }


    public boolean isEmptyPatternSupported() {
        return false;
    }

    @Override
    public ListCellRenderer<Object> getElementsRenderer() {
        return new SearchEverywherePsiRenderer(this) {

            public @NotNull ItemMatchers getItemMatchers(@NotNull JList list, @NotNull Object value) {
                Matcher nameMatcher = MatcherHolder.getAssociatedMatcher(list);
                if (nameMatcher instanceof MinusculeMatcher minusculeMatcher){
                    return new ItemMatchers(minusculeMatcher, PinyinMatcher.create(minusculeMatcher));
                }
                return new ItemMatchers(nameMatcher, nameMatcher);
            }

            @Override
            protected boolean customizeNonPsiElementLeftRenderer(ColoredListCellRenderer renderer, JList list, Object value, int index, boolean selected, boolean hasFocus) {
                try {
                    Color fgColor = list.getForeground();
                    Color bgColor = UIUtil.getListBackground();
                    SimpleTextAttributes nameAttributes = new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, fgColor);

                    ItemMatchers itemMatchers = getItemMatchers(list, value);

                    DocSearchItem docSearchItem = (DocSearchItem) value;
                    String name = docSearchItem.getName();
                    String locationString = docSearchItem.getLocationString();


                    SpeedSearchUtil.appendColoredFragmentForMatcher(name, renderer, nameAttributes, itemMatchers.nameMatcher  , bgColor, selected);
                    renderer.setIcon(docSearchItem.getIcon());


                    if (StringUtils.isNotBlank(locationString)) {
                        locationString = " [" + locationString + "]";
                        FontMetrics fm = list.getFontMetrics(list.getFont());
                        int maxWidth = list.getWidth() - fm.stringWidth(name) - myRightComponentWidth - 36;
                        int fullWidth = fm.stringWidth(locationString);
                        if (fullWidth < maxWidth) {
                            SpeedSearchUtil.appendColoredFragmentForMatcher(locationString, renderer, SimpleTextAttributes.GRAYED_ATTRIBUTES, itemMatchers.locationMatcher, bgColor, selected);
                        } else {
                            int adjustedWidth = Math.max(locationString.length() * maxWidth / fullWidth - 1, 3);
                            locationString = StringUtil.trimMiddle(locationString, adjustedWidth);
                            SpeedSearchUtil.appendColoredFragmentForMatcher(locationString, renderer, SimpleTextAttributes.GRAYED_ATTRIBUTES, itemMatchers.locationMatcher, bgColor, selected);
                        }
                    }


                    return true;
                } catch (Throwable ex) {
                    //ExceptionUtil.handleException(ex);
                    return false;
                }
            }

            @Override
            protected DefaultListCellRenderer getRightCellRenderer(Object value){
                return new DefaultListCellRenderer(){
                    @Override
                    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                        DocSearchItem docSearchItem = (DocSearchItem) value;

                        JLabel jLabel = (JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                        jLabel.setText(docSearchItem.getTailString());
                        return jLabel;
                    }
                };
            }
        };
    }
}
